﻿#include "TreeQueen.h"
#include "../../MirEnvir/Envir.h"
#include "../../../Shared/Functions/Functions.h"
#include "../../../Shared/ServerPackets.h"
#include "../../../Shared/Data/Stat.h"
#include "../DelayedAction.h"
#include "../SpellObject.h"
#include "../../Settings.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
namespace S = ServerPackets;

namespace Server::MirObjects::Monsters
{

    bool TreeQueen::getCanMove() const
    {
        return false;
    }

    bool TreeQueen::getCanRegen() const
    {
        return false;
    }

    TreeQueen::TreeQueen(MonsterInfo *info) : MonsterObject(info)
    {
        setDirection(MirDirection::Up);
    }

    bool TreeQueen::InAttackRange()
    {
        return getTarget()->getCurrentMap() == getCurrentMap();
    }

    void TreeQueen::Turn(MirDirection dir)
    {
    }

    bool TreeQueen::Walk(MirDirection dir)
    {
        return false;
    }

    int TreeQueen::Attacked(MonsterObject *attacker, int damage, DefenceType type)
    {
        return MonsterObject::Attacked(attacker, damage, type);
    }

    int TreeQueen::Attacked(PlayerObject *attacker, int damage, DefenceType type, bool damageWeapon)
    {
        return MonsterObject::Attacked(attacker, damage, type, damageWeapon);
    }

    void TreeQueen::ApplyPoison(Poison *p, MapObject *Caster, bool NoResist, bool ignoreDefence)
    {

    }

    void TreeQueen::Attack()
    {
        if (!getTarget()->IsAttackTarget(this))
        {
            setTarget(nullptr);
            return;
        }

        ShockTime = 0;

        ActionTime = getEnvir()->getTime() + 500;
        AttackTime = getEnvir()->getTime() + AttackSpeed;

        bool ranged = getCurrentLocation() == getTarget()->getCurrentLocation() || !Functions::InRange(getCurrentLocation(), getTarget()->getCurrentLocation(), 2);

        if (!ranged)
        {
            _notNear = false;

            if (getEnvir()->Random->Next(2) > 0)
            {
                // Fire Bombardment Spell
                S::ObjectAttack *tempVar = new S::ObjectAttack();
                tempVar->ObjectID = ObjectID;
                tempVar->Direction = getDirection();
                tempVar->Location = getCurrentLocation();
                tempVar->Type = 0;
                Broadcast(tempVar);

                int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
                if (damage == 0)
                {
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
                    return;
                }

                DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + 500, {getTarget(), damage, DefenceType::MACAgility, true, false});
                ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            }
            else
            {
                // Push Attack
                S::ObjectAttack *tempVar2 = new S::ObjectAttack();
                tempVar2->ObjectID = ObjectID;
                tempVar2->Direction = getDirection();
                tempVar2->Location = getCurrentLocation();
                tempVar2->Type = 1;
                Broadcast(tempVar2);

                int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MaxDC]);
                if (damage == 0)
                {
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
                    return;
                }

                DelayedAction *action = new DelayedAction(DelayedType::Damage, getEnvir()->getTime() + 500, {getTarget(), damage, DefenceType::MACAgility, false, true});
                ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
            }
        }
        else
        {
            _notNear = true;
        }
    }

    void TreeQueen::CompleteAttack(std::vector<std::any> &data)
    {
        MapObject *target = std::any_cast<MapObject*>(data[0]);
        int damage = std::any_cast<int>(data[1]);
        DefenceType defence = std::any_cast<DefenceType>(data[2]);
        bool fireBombardment = std::any_cast<bool>(data[3]);
        bool pushAttack = std::any_cast<bool>(data[4]);

        if (target == nullptr || !target->IsAttackTarget(this) || target->getCurrentMap() != getCurrentMap() || target->Node == nullptr)
        {
            return;
        }

        if (fireBombardment)
        {
            std::vector<MapObject*> targets = FindAllTargets(3, getCurrentLocation());
            if (targets.empty())
            {
                return;
            }

            for (int i = 0; i < targets.size(); i++)
            {
                targets[i]->Attacked(this, damage, defence);
            }
        }

        if (pushAttack)
        {
            std::vector<MapObject*> targets = FindAllTargets(1, getCurrentLocation());
            if (targets.empty())
            {
                return;
            }

            for (int i = 0; i < targets.size(); i++)
            {
                targets[i]->Pushed(this, Functions::DirectionFromPoint(getCurrentLocation(), targets[i]->getCurrentLocation()), 5);
            }
        }
    }

    void TreeQueen::SpawnRoots()
    {
        if (Dead)
        {
            return;
        }

        int count = getEnvir()->Random->Next(1, _rootCount);
        int distance = getEnvir()->Random->Next(_rootSpreadMin, _rootSpreadMax);

        for (int j = 0; j < getCurrentMap()->Players.size(); j++)
        {
            Point *playerLocation = getCurrentMap()->Players[j]->getCurrentLocation();

            bool hit = false;

            for (int i = 0; i < count; i++)
            {
                Point *location = new Point(playerLocation->X + getEnvir()->Random->Next(-distance, distance + 1), playerLocation->Y + getEnvir()->Random->Next(-distance, distance + 1));

                if (getEnvir()->Random->Next(3) == 0)
                {
                    location = playerLocation;
                    hit = true;
                }

                if (!getCurrentMap()->ValidPoint(location))
                {
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
                    continue;
                }

                auto start = getEnvir()->Random->Next(2000);

                SpellObject *spellObj = new SpellObject();
                spellObj->Spell = Spell::TreeQueenRoot;
                spellObj->Value = getEnvir()->Random->Next(getEnvir()->Random->Next(Stats[Stat::MinDC], Stats[Stat::MaxDC]));
                spellObj->ExpireTime = getEnvir()->getTime() + 1500 + start;
                spellObj->TickSpeed = 2000;
                spellObj->Caster = nullptr;
                spellObj->setCurrentLocation(location);
                spellObj->setCurrentMap(getCurrentMap());
                spellObj->setDirection(MirDirection::Up);

                DelayedAction *action = new DelayedAction(DelayedType::Spawn, getEnvir()->getTime() + start, {spellObj});
                getCurrentMap()->ActionList.push_back(action);

                if (hit)
                {
//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete spellObj' statement was not added since spellObj was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
                    break;
                }

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete spellObj' statement was not added since spellObj was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
            }
        }
    }

    void TreeQueen::SpawnMassRoots()
    {
        if (Dead)
        {
            return;
        }

        S::ObjectRangeAttack *tempVar = new S::ObjectRangeAttack();
        tempVar->ObjectID = ObjectID;
        tempVar->Direction = getDirection();
        tempVar->Location = getCurrentLocation();
        tempVar->TargetID = getTarget()->ObjectID;
        tempVar->Type = 1;
        Broadcast(tempVar);

        auto count = getCurrentMap()->Players.size();

        if (count == 0)
        {
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            return;
        }

        auto target = getCurrentMap()->Players[getEnvir()->Random->Next(count)];

        auto location = target->getCurrentLocation();

        for (int y = location->Y - 3; y <= location->Y + 3; y++)
        {
            if (y < 0)
            {
                continue;
            }
            if (y >= getCurrentMap()->Height)
            {
                break;
            }

            for (int x = location->X - 3; x <= location->X + 3; x++)
            {
                if (x < 0)
                {
                    continue;
                }
                if (x >= getCurrentMap()->Width)
                {
                    break;
                }

                if (x == getCurrentLocation()->X && y == getCurrentLocation()->Y)
                {
                    continue;
                }

                auto cell = getCurrentMap()->GetCell(x, y);

                if (!cell->getValid())
                {
                    continue;
                }

                int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MinDC]);

                auto start = 500;

                SpellObject *ob = new SpellObject();
                ob->Spell = Spell::TreeQueenMassRoots;
                ob->Value = damage;
                ob->ExpireTime = getEnvir()->getTime() + 1500 + start;
                ob->TickSpeed = 1000;
                Point tempVar2(x, y);
                ob->setCurrentLocation(&tempVar2);
                ob->CastLocation = location;
                ob->Show = location->X == x && location->Y == y;
                ob->setCurrentMap(getCurrentMap());

                DelayedAction *action = new DelayedAction(DelayedType::Spawn, getEnvir()->getTime() + start, {ob});
                getCurrentMap()->ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
            }
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    void TreeQueen::SpawnGroundRoots()
    {
        if (Dead)
        {
            return;
        }

        for (int i = 0; i < getCurrentMap()->Players.size(); i++)
        {
            Point *location = new Point(getCurrentLocation()->X + getEnvir()->Random->Next(-_groundrootSpread, _groundrootSpread + 1), getCurrentLocation()->Y + getEnvir()->Random->Next(-_groundrootSpread, _groundrootSpread + 1));

            for (int y = location->Y - 2; y <= location->Y + 2; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = location->X - 2; x <= location->X + 2; x++)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }

                    if (x == getCurrentLocation()->X && y == getCurrentLocation()->Y)
                    {
                        continue;
                    }

                    auto cell = getCurrentMap()->GetCell(x, y);

                    if (!cell->getValid())
                    {
                        continue;
                    }

                    int damage = GetAttackPower(Stats[Stat::MinDC], Stats[Stat::MinDC]);

                    auto start = getEnvir()->Random->Next(4000);

                    SpellObject *ob = new SpellObject();
                    ob->Spell = Spell::TreeQueenGroundRoots;
                    ob->Value = damage;
                    ob->ExpireTime = getEnvir()->getTime() + 900 + start;
                    ob->TickSpeed = 1000;
                    Point tempVar(x, y);
                    ob->setCurrentLocation(&tempVar);
                    ob->CastLocation = location;
                    ob->Show = location->X == x && location->Y == y;
                    ob->setCurrentMap(getCurrentMap());

                    DelayedAction *action = new DelayedAction(DelayedType::Spawn, getEnvir()->getTime() + start, {ob});
                    getCurrentMap()->ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                }
            }

//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was assigned to a field. Handle memory management manually.
        }
    }

    Packet *TreeQueen::GetInfo()
    {
        S::ObjectMonster *tempVar = new S::ObjectMonster();
        tempVar->ObjectID = ObjectID;
        tempVar->Name = getName();
        tempVar->NameColour = NameColour;
        tempVar->Location = getCurrentLocation();
        tempVar->Image = Info->Image;
        tempVar->Direction = getDirection();
        tempVar->Effect = Info->Effect;
        tempVar->AI = Info->AI;
        tempVar->Light = Info->Light;
        tempVar->Dead = Dead;
        tempVar->Skeleton = Harvested;
        tempVar->Poison = CurrentPoison;
        tempVar->Hidden = getHidden();

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }

    void TreeQueen::Spawned()
    {
        // Begin timers (stops players from being bombarded with attacks when they enter the room / map).
        _rootSpawnTime = getEnvir()->getTime() + (Settings::Second * 5);
        _groundRootSpawnTime = getEnvir()->getTime() + (Settings::Second * 15);

        MonsterObject::Spawned();
    }

    void TreeQueen::ProcessTarget()
    {
        if (getCurrentMap()->Players.empty())
        {
            return;
        }

        if (getEnvir()->getTime() > _rootSpawnTime)
        {
            if (getEnvir()->Random->Next(4) > 0)
            {
                SpawnRoots();
            }
            else
            {
                SpawnMassRoots();
            }

            auto next = getEnvir()->Random->Next(1, 4);

            _rootSpawnTime = getEnvir()->getTime() + (Settings::Second * (_notNear ? next : next * _nearMultiplier));
        }

        if (getEnvir()->getTime() > _groundRootSpawnTime)
        {
            SpawnGroundRoots();

            auto next = getEnvir()->Random->Next(2, 3);

            _groundRootSpawnTime = getEnvir()->getTime() + (Settings::Second * (_notNear ? next : next * _nearMultiplier));
        }

        if (!getCanAttack())
        {
            return;
        }

        if (getTarget() == nullptr)
        {
            return;
        }

        if (InAttackRange() && getCanAttack())
        {
            Attack();
            return;
        }
    }
}
